package com.jse.web;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import com.jse.Fs;
import com.jse.IP;
import com.jse.Ioc;
import com.jse.Js;
import com.jse.Jse;
import com.jse.Lang;
import com.jse.Log;
import com.jse.Strings;

@SuppressWarnings("unchecked")
public class Web {

	private static Log log=Log.get("Web");
	public final static Map<String,Object> SESSIONS=new ConcurrentHashMap<>();//所有创建的session
	public static Object sc;//servletContext 仅在servlet,jakarta下生效
	public static boolean upload_auto=Jse.conf.getBoolean("jse.upload.auto",false);//req根据请求自动上传
	private static String ipcheck=Jse.conf.getString("jse.ipcheck");//ip检查
	public static boolean cors_allow=Jse.conf.containsPrifix("jse.cors");
	public static boolean has_mobile=false;//包含手机目录
	public static String mobile_dir="m";//包含手机目录
	public static String filterall;//拦截全部的js
	public final static Map<String,Object> data=new HashMap<>();//公共数据,自定义server时的servletContent.attr
	private final static ScopedValue<Ctx> RQ =  ScopedValue.newInstance();//虚拟线程下的req,resp封装
	public static boolean start=false;
	
	public final static Set<String> STATIC_PRIFIX=Lang.ofSet("/favicon.ico","/upload/","/assets/","/image/","/img/","/js/","/css/",
			"/font/","/fonts/","/public/","/static/","/404.html","/403.html","/500.html");
	public final static Set<String> STATIC_SUFFIX=Lang.ofSet("ico","css","js","htm","txt","mjs","xml",
			"jpg","png","jpeg","gif","webp","bmp","svg","swf","map","crdownload",
			"aac","avi","mp4","mp3","mdi","csv","doc","docx","xls","xlsx","ppt","pptx","rtf","wav","pdf",
			"otf","tif","ttf","eot","tiff","woff","woff2","7z","rar","tar","zip","yml"
			);//后缀可定制取消
	
	public final static HashMap<String,String> filters=new HashMap<>();
	public static Ctx ctx() {return RQ.get();}
	public static Object data(String name) {return data.get(name);}
	public static Map<String,Object> data(String name,Object value) {data.put(name, value);return data;}
	public static Object attr(String name) {return RQ.get().getAttribute(name);}
	public static Ctx attr(String name,Object value) {var c=RQ.get();c.setAttribute(name, value);return c;}
	public static Object sattr(String name) {return RQ.get().sattr(name);}
	public static Ctx sattr(String name,Object value) {var c=RQ.get();c.sattr(name, value);return c;}
	public static Object app(String name) {return RQ.get().app(name);}
	public static Ctx app(String name,Object value) {var c=RQ.get();c.app(name, value);return c;}
	public static Map<String, Object> datas(Map<String, Object> map){
		data.forEach((k,v)->map.putIfAbsent(k, v));
		var req=RQ.get();
		map.put("cache",Ioc.get("cache"));
		map.put("p",req);
		map.put("session",req.sessionMap());
		return map;
	}
	
	/**
	 * 设置跨域 conf中以jse.cors.为前缀 <br/>
	 * 允许跨域的主机 Access-Control-Allow-Origin * <br/>
	 * 允许跨域的请求方法GET, POST, HEAD 等 Access-Control-Allow-Methods * <br/>
	 * 重新预检验跨域的缓存时间(s) Access-Control-Max-Age 3600 <br/>
	 * 允许跨域的请求头 Access-Control-Allow-Headers * <br/>
	 * 是否携带cookie Access-Control-Allow-Credentials true
	 */
	public static void cors(Ctx tbl) {
		tbl.setHeader("Access-Control-Allow-Origin",Jse.conf.getString("jse.cors.Access-Control-Allow-Origin","*"));
		tbl.setHeader("Access-Control-Allow-Methods",Jse.conf.getString("jse.cors.Access-Control-Allow-Methods","*"));
		tbl.setHeader("Access-Control-Max-Age",Jse.conf.getString("jse.cors.Access-Control-Max-Age","3600"));
		tbl.setHeader("Access-Control-Allow-Headers",Jse.conf.getString("jse.cors.Access-Control-Allow-Headers","*"));
		tbl.setHeader("Access-Control-Allow-Credentials",Jse.conf.getString("jse.cors.Access-Control-Allow-Origin","false"));
	}
	
	public static String mapping(String url,String suffix,Map<String,Object> tbl) {//TODO 暂只支持最后一段path
		String s=url;if(s.endsWith("/"))s+="index";
		s=s.endsWith("."+suffix)?s.substring(0,s.length()-(suffix.length()+1)):s;
		int index=s.lastIndexOf('/');
		var s1=s.substring(index+1);
		if(Strings.isNumber(s1)){
			var id=Integer.valueOf(s1);
			tbl.put("id",id);
			if(suffix.length()>0)return s.substring(0,index);
			return s.substring(0,index)+"/id";
		}
		if(Strings.hasUpperCase(s1)||Strings.hasChinese(s1)){//包含大写或中文
			tbl.put("name",s1);
			if(suffix.length()>0)return s.substring(0,index);
			return s.substring(0,index)+"/name";
		}
		return s;
	}
	public static boolean isStatic(String url,String suffix) {return Lang.startsWith(url, STATIC_PRIFIX)||STATIC_SUFFIX.contains(suffix);}
	
	public static String filter(Ctx tbl) {
		var filtermapping=Lang.startsWithGet(tbl.getPath(),filters.keySet());
		if(filtermapping==null)filtermapping=filterall;
		return filtermapping==null?filterall:filtermapping;
	}
	
	public static void init() {
		Web.start=true;
		if(Fs.exists("js","jse.js"))Js.eval(Fs.readString("js","jse.js"));
		Web.data("root",Jse.conf.getString("jse.web.root","/"));
		Fs.paths(Path.of(Jse.jspath(),"$"), 0).forEach(x->{
			String f=x.getFileName().toString();
			if("$.js".equals(f))filterall=x.toString();//拦截公共
			else filters.put("/"+f.replace(".js","/"),x.toString());
		});
		log.debug("init web jsefilter:%s",filters.keySet());
		has_mobile=Files.exists(Path.of(Jse.webapp(),"m"));
		if(Jse.conf.containsKey("jse.jse.npm"))Js.jnpm();
		if(Jse.conf.getBoolean("jse.jse.babel",false))Js.babel();
		Router.addRoute("/jse/**",Web::jse);
	}
	
	public static void service(Ctx tbl) throws IOException {
		String path=tbl.getPath();
		if(!IP.ipCheck(tbl.getRemoteAddr(),ipcheck,tbl.getResponseBody()))return;//IP功能
		if(cors_allow)Web.cors(tbl);//设置跨域
		if(isStatic(path,tbl.getSuffix())) {View.staticforward.render(tbl,path);return;}
		try {
			ScopedValue.where(RQ,tbl).call(()->{
					var filtermapping=filter(tbl);
					if(filtermapping!=null) {
						Object x=Js.run(filters.get(filtermapping),tbl);
						if(!Boolean.TRUE.equals(x)) {
							View.filter.render(tbl,x);
							return false;
						}
					}
					Object o=Router.getRoute(tbl).run(tbl);
					if(o instanceof Boolean b) {
						return b;
					}else{
						View.ret.render(tbl, o);
						return false;
					}
				});
			} catch (Throwable e) {
				e.printStackTrace();
//				String s=Errs.message(e);
//				log.error(s);
//				View.text.render(tbl,s);
			} finally {
				tbl.clear();
				log.info("%s %dms",tbl.getPath(),(System.currentTimeMillis()-tbl.time()));
			}
	}
	public static boolean isAllowFormData(Ctx ctx) {
		if(sattr("admin")!=null)return true;//后台登录后允许
		if(sattr("user")!=null)return true;//前台登录后允许
		if(ctx.getPath().equals("/jse/up")&&upload_auto)return true;//jse/up 且开启了自动上传
		return false;
	}
	
	public static boolean jse(Ctx tbl){
		try {
			if("/jse".equals(tbl.getServletPath())) {
				var bind=Lang.ofMap("req",tbl.req(),"resp",tbl.res(),"session",tbl.getSession());
				Js.eval(Fs.readString("js","jse.js"),bind);
				View.text.render(tbl,"数据已更新，请关闭当前页面后重试！");
			}else if(tbl.getServletPath().equals("/jse/up")){
				View.json.render(tbl,tbl);
			}else if(tbl.getServletPath().equals("/jse/captcha")){
				View.captcha.render(tbl,Lang.randomString(4));
			}else if(tbl.getServletPath().startsWith("/jse/captcha/")){
				String code=tbl.getServletPath().substring(13);
				if(code.isEmpty())View.captcha.render(tbl,"empty");
				else View.captcha.render(tbl,code);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return false;
	}
}
